////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    CrystalWatersGuts.cpp	     								  //
//  Developer:   Ryan Sauls												      //
//  Create date: 7/8/2006                                                     //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Crystal Waters.               //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        Copyright (c) 2006 - Realtime Gaming.  All rights reserved.         //
////////////////////////////////////////////////////////////////////////////////

#include "CrystalWatersGuts.h"

#pragma region Static Array Initializations

////////////////////////////////////////////////////////////////////////////
/// CrystalWatersGuts::::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Nine, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_NINE ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_NINE.
////////////////////////////////////////////////////////////////////////////
CrystalWatersGuts::SpinResult CrystalWatersGuts::m_aLineWinSpinResults[NUM_VALID_ICONS][6] = 
{
// Number of icons:	 0				1					2						3							4						5
/* ICON_NINE	   */{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_NINE,				EVT_FOUR_NINE,			EVT_FIVE_NINE		},
/* ICON_TEN		   */{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_TEN,				EVT_FOUR_TEN,			EVT_FIVE_TEN		},
/* ICON_JACK	   */{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_JACK,				EVT_FOUR_JACK,			EVT_FIVE_JACK		},
/* ICON_QUEEN	   */{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_QUEEN,			EVT_FOUR_QUEEN,			EVT_FIVE_QUEEN		},
/* ICON_KING	   */{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_KING,				EVT_FOUR_KING,			EVT_FIVE_KING		},
/* ICON_ACE		   */{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_ACE,				EVT_FOUR_ACE,			EVT_FIVE_ACE		},
/* ICON_SHELL	   */{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_SHELL,			EVT_THREE_SHELL,			EVT_FOUR_SHELL,			EVT_FIVE_SHELL		},
/* ICON_FISH_SCHOOL*/{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_FISH_SCHOOL,	EVT_THREE_FISH_SCHOOL,		EVT_FOUR_FISH_SCHOOL,	EVT_FIVE_FISH_SCHOOL},
/* ICON_SEA_HORSE  */{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_SEA_HORSE,		EVT_THREE_SEA_HORSE,		EVT_FOUR_SEA_HORSE,		EVT_FIVE_SEA_HORSE	},
/* ICON_TURTLE	   */{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_TURTLE,			EVT_THREE_TURTLE,			EVT_FOUR_TURTLE,		EVT_FIVE_TURTLE		},
/* ICON_YACHT	   */{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,					EVT_LOSER,				EVT_LOSER			},
/* ICON_DOLPHIN	   */{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,					EVT_LOSER,				EVT_LOSER			},
/* ICON_BONUS_FISH */{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,					EVT_LOSER,				EVT_LOSER			}
};

////////////////////////////////////////////////////////////////////////////
/// CrystalWatersGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by icon count only, since Yacht is the only
/// scatter win.  So to get the SpinResult for 5 Yacht, use the
/// following code snippet:
///
///		SpinResult result = m_aScatterWinSpinResults[ 5 ];
///
/// In this example result would be set to EVT_FIVE_YACHT.
////////////////////////////////////////////////////////////////////////////
CrystalWatersGuts::SpinResult CrystalWatersGuts::m_aScatterWinSpinResults[6] = 
{
	// ICON					Number of Icons
	EVT_LOSER,				// 0
	EVT_LOSER,				// 1
	EVT_FEATURE_NEAR_MISS,	// 2
	EVT_THREE_YACHT,		// 3
	EVT_FOUR_YACHT,			// 4
	EVT_FIVE_YACHT			// 5
};

#pragma endregion

#pragma region Construction & Destruction

////////////////////////////////////////////////////////////////////////////
/// @fn 	 CrystalWatersGuts::RainDanceGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
CrystalWatersGuts::CrystalWatersGuts(void)
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 CrystalWatersGuts::~RainDanceGuts()
///
/// Destructor for CrystalWatersGuts. 
////////////////////////////////////////////////////////////////////////////
CrystalWatersGuts::~CrystalWatersGuts(void)
{
}

#pragma endregion

#pragma region Methods

////////////////////////////////////////////////////////////////////////////
/// @fn 	 CrystalWatersGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden to reset the current "Bonus Fish" multiplier
////////////////////////////////////////////////////////////////////////////
void CrystalWatersGuts::preSpinClassifyResults( SlotSpin* spin )
{
	setCurrentFishMultiplier( 0 );
	SlotGuts::preSpinClassifyResults( spin );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 CrystalWatersGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// @return	 uint8      A payout from the SpinResult enumeration as a uint8.
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void CrystalWatersGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare local variables to track whether or not we found any substitution.
	bool bDolphinSubstitution = false;
	bool bBonusFishSubstitution = false;
	
	// Initialize out param
	multiplier = 1;

	// See what the spin result is, and if we used any substitution symbols.
	rtnresult = getLineWinResult( stops, testIcon, bDolphinSubstitution, bBonusFishSubstitution );

#ifdef _DEBUG
	SpinResult r = (SpinResult)rtnresult;
	ASSERT( r >= EVT_LOSER && r < NUM_VALID_SPIN_RESULTS );
#endif

	// If we did actually have a substitution and a winning spin
	if (  ( bDolphinSubstitution || bBonusFishSubstitution ) 
	   && ( EVT_TWO_SHELL <= rtnresult && rtnresult <= EVT_FIVE_TURTLE ) )
	{
		// change the multiplier to reflect which substitution(s) we found
		if ( bDolphinSubstitution )
		{
			multiplier *= NORMAL_MODE_SUBSTITUTION_MULTIPLIER;
			spinline->addNote( NOTE_SUBSTITUTION );
			spinline->addNote( NOTE_2X_DOLPHIN );
		}
		
		// bonus fish multipliers are cumulative along with dolphin multipliers
		if ( bBonusFishSubstitution )
		{
			//Better be a free spin...
			ASSERT( MODE_FREE_SPIN == getGameModeId() );

			//Get the multiplier from the prob table if we don't have one.
			if( !getCurrentFishMultiplier() )
			{
				uint8 fishMultiplier = getRandomValueFromProbabilityTable();
				ASSERT( fishMultiplier == 2 || fishMultiplier == 4 || fishMultiplier == 8 );
				setCurrentFishMultiplier( fishMultiplier );
			}

			multiplier *= getCurrentFishMultiplier();
			spinline->addNote( NOTE_FREEGAME_SUBSTITUTION );
			spinline->addNote( getFreeSpinMultiplierNoteId( getCurrentFishMultiplier() ) );
		}
	}

	ASSERT( EVT_LOSER <= rtnresult && rtnresult <= EVT_FIVE_TURTLE );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 CrystalWatersGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void CrystalWatersGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Keep up with how many scatter icons where found.
	uint8 numScatterIcons = 0;

	// Declare a local variable to track the scatter positions.
	int32 nScatterPositions = 0;

	// See what the spin result is.
	SpinResult srResult = getScatterWinResult( spin, nScatterPositions, numScatterIcons );

	// Give them the scatter prize.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	// See if they triggered the feature.
	evaluateFeatureTrigger( spin, srResult, nScatterPositions, numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn		CrystalWatersGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void CrystalWatersGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( spin->getSpinLineCount() ) || 
			m_bForceRandomJackpot)
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }
}


////////////////////////////////////////////////////////////////////////////
/// @fn 	 CrystalWatersGuts::evaluateFeatureTrigger()
///
/// This function determines if the feature is triggered.
////////////////////////////////////////////////////////////////////////////
void CrystalWatersGuts::evaluateFeatureTrigger( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions, uint8 numScatterIcons )
{
	// see if they triggered the free-spin feature.
	if ( srScatterResult == EVT_THREE_YACHT ||
		 srScatterResult == EVT_FOUR_YACHT  ||
		 srScatterResult == EVT_FIVE_YACHT )
	{
		// They triggered the free-spin feature.
		// Make sure we are in free-spin mode.
		setGameModeId( MODE_FREE_SPIN );

		//Add the Free Spins.  There are 15.
		incrementFreeSpins( 15 );
		
		// Add a scatter to the spin for the feature trigger.
		spin->addScatter( scatterPositions, 0, 0, false,
			EVT_FREE_GAMES_FEATURE, 15 );
	}
	//otherwise, two scatter icons constitutes a near miss.
	else if ( numScatterIcons >= 1 )
	{
		// then add a scatter to the spin for the feature near miss.
		spin->addScatter( scatterPositions, 0, 0, false,
			EVT_FEATURE_NEAR_MISS, 0 );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 CrystalWatersGuts::evaluateScatterWin(SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions)
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void CrystalWatersGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions )
{
	// If we got the scatter win (or almost got it)
	if ( srScatterResult != EVT_LOSER )
	{
		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;

		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= spin->getSpinLineCount();

		// Add the payout to the total win for this spin.
		increaseTotalWin( nPayout );

		// Add a scatter to the spin.
		spin->addScatter( scatterPositions,
			nPayout * getBetPerLine(), 1, false, srScatterResult, 0 );
	}
}


////////////////////////////////////////////////////////////////////////////
/// @fn		CrystalWatersGuts::getScatterWinResult()
///
/// @param   spin				The spin we are evaluating.
/// @param   scatterPositions	An output parameter that contains a bit mask for the icon positions.
/// @param	 bSubstitition		An output parameter that is set to true if a substitution symbol was used.
/// @param	 numScatterIcons	An output parameter that is set to the number of scatter icons that were found.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
///	This function looks for scatter wins.
////////////////////////////////////////////////////////////////////////////
CrystalWatersGuts::SpinResult
CrystalWatersGuts::getScatterWinResult( SlotSpin* spin, int32& scatterPositions, uint8& numScatterIcons ) const
{
	// Keep up with how many scatter icons we found.
	numScatterIcons = 0;
	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Keep up with whether or not we found a scatter icon on this stop.
			bool bFoundScatterIconOnReel = false;

			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is a Yacht...
			if ( currentIcon == ICON_YACHT )
			{
				// Set the flag so that we know we found a scatter icon on this reel.
				bFoundScatterIconOnReel = true;
			}

			// If we found a scatter icon...
			if ( bFoundScatterIconOnReel )
			{
				// then increase the scatter icon count
				numScatterIcons++;

				// Set the bit in the position mask for this stop.
				scatterPositions |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;

		}
	}

	// Use the array to look up the spin result in the current game mode.
	return lookupScatterWinSpinResult( numScatterIcons );
}

//////////////////////////////////////////////////////////////////////////
/// @fn		CrystalWatersGuts::getLineWinResult()
///
/// @param   stops				The line stops we are evaluating.
/// @param   testIcon			Which icon we are looking for in this line.
/// @param	 bDolphinSubstitution		An output parameter to indicate if a Dolphin substitution symbol was used.
/// @param	 bBonusFishSubstitution		An output parameter to indicate if a Fish substitution symbol was used.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
///	This function looks for line wins.
////////////////////////////////////////////////////////////////////////////
CrystalWatersGuts::SpinResult 
CrystalWatersGuts::getLineWinResult( uint8* stops, uint8 testIcon, 
									 bool& bDolphinSubstitution /*out value*/,
									 bool& bBonusFishSubstitution /*out value*/)
{
	//Since neither of the substitution icons may appear on reel one,
	//and this game is left-to-right only,
	//We assume that we will never be using them as the test icon.
	ASSERT( testIcon != ICON_DOLPHIN );
	ASSERT( testIcon != ICON_BONUS_FISH );

	// Initialize the out parameters
	bDolphinSubstitution = bBonusFishSubstitution = false;

	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
	{
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Dolphin or Bonus Fish...
		if ( currentIcon == ICON_DOLPHIN || currentIcon == ICON_BONUS_FISH )
		{
			#ifdef DEBUG
				//Check that it's the reel & game mode we expect
				if ( ICON_DOLPHIN == currentIcon )
				{
					ASSERT( i == 1 || i == 3 );
				}
				else if ( ICON_BONUS_FISH == currentIcon )
				{
					ASSERT( getGameModeId() == MODE_FREE_SPIN && i == 2 );
				}
			#endif

		    // Doesn't substitute for Yacht
			if ( testIcon != ICON_YACHT )
			{
				// then use it as a substitution symbol and continue on.				
				bDolphinSubstitution |= currentIcon == ICON_DOLPHIN;
				bBonusFishSubstitution |= currentIcon == ICON_BONUS_FISH;
				continue;
			}
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			// then the streak is over so break out of the loop
			break;
        }
	}

	// Use the array to look up the spin result for i testIcons.
	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 CrystalWatersGuts::Debug_SetLoserStops()   
///
/// @param	 pStops      Array into which to put the stop values    
///
/// This overridden method returns a losing set of stop values
////////////////////////////////////////////////////////////////////////////
void CrystalWatersGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=1;
	pStops[1]=0;
	pStops[2]=0;
	pStops[3]=0;
	pStops[4]=0;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 CrystalWatersGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 CrystalWatersGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 scatter icons.
	ASSERT( countof( m_aScatterWinSpinResults ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that all game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range.
	return ( getGutsId() >= CrystalWatersGuts::VALID_GUTS_RANGE_BEGIN && getGutsId() < CrystalWatersGuts::VALID_GUTS_RANGE_END );
}

#pragma region Debug Spins Support

void CrystalWatersGuts::initDebugFunctionMap()
{
	super::initDebugFunctionMap();

	addDebugStopFunction( EVT_DEBUG_FUNCTION_WIN_RANDOM, 
		new DebugStopFunc<CrystalWatersGuts>(this, &CrystalWatersGuts::DebugFeature_WinRandom) );
	addDebugStopFunction( EVT_DEBUG_FUNCTION_WIN_TWO, 
		new DebugStopFunc<CrystalWatersGuts>(this, &CrystalWatersGuts::DebugFeature_WinTwo) );
	addDebugStopFunction( EVT_DEBUG_FUNCTION_WIN_FOUR, 
		new DebugStopFunc<CrystalWatersGuts>(this, &CrystalWatersGuts::DebugFeature_WinFour) );
	addDebugStopFunction( EVT_DEBUG_FUNCTION_WIN_EIGHT, 
		new DebugStopFunc<CrystalWatersGuts>(this, &CrystalWatersGuts::DebugFeature_WinEight) );
}

void CrystalWatersGuts::DebugFeature_WinRandom( SlotSpin *spin )
{
	setCurrentFishMultiplier( 0 );
}

void CrystalWatersGuts::DebugFeature_WinTwo( SlotSpin *spin )
{
	setCurrentFishMultiplier( 2 );
}

void CrystalWatersGuts::DebugFeature_WinFour( SlotSpin *spin )
{
	setCurrentFishMultiplier( 4 );
}

void CrystalWatersGuts::DebugFeature_WinEight( SlotSpin *spin )
{
	setCurrentFishMultiplier( 8 );
}

#pragma endregion

#pragma endregion